
#ifndef __MATERIAL_H__
#define __MATERIAL_H__

#include "rt.hh"

struct hit_record;

class material {
	public : 
		virtual bool scatter(const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered) const = 0;
};

class lambertion : public material {
	public : 
		color albedo;

	public : 
		lambertion(const color& c) : albedo{c} {} 

		virtual bool scatter(const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered) const override {
			vec3 scatter_dir = rec.normal + rand_unit_vec();

			if (scatter_dir.near_zero()) { scatter_dir = rec.normal; }
			scattered = ray{rec.point, scatter_dir};
			attenuation = albedo;

			return true;
		}
};

class metal : public material {
	public : 
		color albedo;
		double fuzz;

	public : 
		metal(const color& c, double f) : albedo{c}, fuzz{f < 1 ? f : 1} {}

		virtual bool scatter(const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered) const override { 
			vec3 reflection = reflect(unit_vec(r_in.direction()), rec.normal);
			scattered = ray{rec.point, reflection + fuzz * rand_in_unit_sphere()};
			attenuation = albedo;
	
			return (dot(scattered.direction(), rec.normal) > 0);
		}
};

class dielectric : public material {
	public : 
		double ir;

	public : 
		dielectric(double _ir) : ir{_ir} {}

		virtual bool scatter(const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered) const override { 
			attenuation = color{1.0, 1.0, 1.0};
			double refraction_ratio = rec.front_face ? (1.0  / ir) : ir; 

			vec3 unit_dir = unit_vec(r_in.direction());
			double cos_theta = fmin(dot(-unit_dir, rec.normal), 1.0);	
			double sin_theta = std::sqrt(1.0 - cos_theta * cos_theta);

			vec3 dir;
			if ((refraction_ratio * sin_theta > 1.0) || (reflectance(cos_theta, refraction_ratio) > rand_double())) 
				{ dir = reflect(unit_dir, rec.normal); }
			else { dir = refract(unit_dir, rec.normal, refraction_ratio); }

			scattered = ray{rec.point, dir};
			return true;
		}

	private:
		   static double reflectance(double cosine, double ref_idx) {
				auto r0 = (1 - ref_idx) / (1 + ref_idx);
		    	r0 = r0 * r0;
				return r0 + (1 - r0) * pow((1 - cosine), 5);
			}
};

#endif
